package com.tcm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tcm.common.enums.CachePrefix;
import com.tcm.config.AppProperties;
import com.tcm.entity.*;
import com.tcm.entity.bo.SocialStudentRankCacheBo;
import com.tcm.entity.bo.StudentRankCacheBo;
import com.tcm.entity.bo.UserCacheBo;
import com.tcm.entity.dto.DayRange;
import com.tcm.entity.dto.ScoreUpdateDto;
import com.tcm.entity.dto.param.SocialStudentPageParam;
import com.tcm.entity.dto.param.StudentPageParam;
import com.tcm.entity.resp.PageR;
import com.tcm.entity.vo.*;
import com.tcm.exception.DatabaseOperationException;
import com.tcm.mapper.ScoreMapper;
import com.tcm.service.*;
import com.tcm.utils.DateTimeUtil;
import com.tcm.utils.RedisUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Guqier
 * @description 针对表【ts_score(积分记录表)】的数据库操作Service实现
 * @createDate 2023-08-01 17:23:38
 */
@Service
public class ScoreServiceImpl extends ServiceImpl<ScoreMapper, Score> implements ScoreService {

    @Autowired
    private StudentService studentService;

    @Autowired
    private SocialStudentService socialStudentService;

    @Autowired
    private SocialClazzService socialClazzService;

    @Autowired
    private RankService rankService;

    @Autowired
    private UserService userService;

    @Autowired
    private ImageService imageService;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 获取学生积分查询列表
     * @param param 查询参数
     * @return 学生积分查询列表
     */
    @Override
    public PageR<StudentScoreListResult> getStudentRankList(StudentPageParam param) {
        // 获取积分等级
        List<Rank> ranks = rankService.list();
        // 获取指定学生信息
        PageR<StudentListResult> result = studentService.getStudentListByPage(param);
        PageR<StudentScoreListResult> pageR = new PageR<>();
        BeanUtils.copyProperties(result, pageR, "records");
        List<StudentScoreListResult> collect = result.getRecords().stream().map(studentListResult -> {
            StudentScoreListResult studentScoreListResult = new StudentScoreListResult();
            BeanUtils.copyProperties(studentListResult, studentScoreListResult, "rankName");
            Integer score = studentScoreListResult.getScore();
            // 匹配积分等级
            ranks.stream()
                    .filter(rank -> (rank.getUpperLimit().compareTo(score) <= 0) && rank.getLowerLimit().compareTo(score) > 0)
                    .findFirst()
                    .ifPresent(rank -> studentScoreListResult.setRankName(rank.getName()));
            return studentScoreListResult;
        }).sorted().collect(Collectors.toList());
        pageR.setRecords(collect);
        return pageR;
    }

    /**
     * 获取社会化学员积分查询列表
     * @param param 查询参数
     * @return 社会化学员积分查询列表
     */
    @Override
    public PageR<SocialStudentScoreListResult> getSocialStudentRankList(SocialStudentPageParam param) {
        // 获取积分等级
        List<Rank> ranks = rankService.list();
        // 获取社会化学员列表
        PageR<SocialStudentListResult> socialStudentList = socialStudentService.getSocialStudentListByPage(param);
        PageR<SocialStudentScoreListResult> pageR = new PageR<>();
        BeanUtils.copyProperties(socialStudentList, pageR, "records");
        pageR.setRecords(socialStudentList.getRecords().stream().map(socialStudentListResult -> {
            SocialStudentScoreListResult result = new SocialStudentScoreListResult();
            BeanUtils.copyProperties(socialStudentListResult, result, "rankName");
            Integer score = socialStudentListResult.getScore();
            // 匹配积分等级
            ranks.stream()
                    .filter(rank -> (rank.getUpperLimit().compareTo(score) <= 0) && rank.getLowerLimit().compareTo(score) > 0)
                    .findFirst()
                    .ifPresent(rank -> result.setRankName(rank.getName()));
            return result;
        }).sorted().collect(Collectors.toList()));
        return pageR;
    }

    /**
     * 根据学生积分排行存储到缓存
     */
    @Override
    public void saveStudentRankToCache() {
        LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(Student::getScore);
        List<Student> students = studentService.list(queryWrapper);
        List<User> users = userService.list();
        List<StudentRankCacheBo> bos = students.stream().map(student -> {
            StudentRankCacheBo bo = new StudentRankCacheBo();
            BeanUtils.copyProperties(student, bo);
            users.stream().filter(user -> user.getAccount().equals(student.getAccount())).findFirst().ifPresent(user -> {
                if (Objects.nonNull(user.getAvatar())) {
                    Image image = imageService.getImageById(user.getAvatar());
                    if (Objects.nonNull(image)) {
                        bo.setAvatarUrl(image.getRemoteFileUrl());
                    }
                }
                bo.setNickname(user.getNickname());
            });
            return bo;
        }).collect(Collectors.toList());
        redisUtil.setCacheObject(CachePrefix.STU_SCORE_RANK.getPrefix(), bos, 25, TimeUnit.HOURS);
    }

    /**
     * 根据社会化学员积分排行存储到缓存
     */
    @Override
    public void saveSocialStudentRankToCache() {
        LambdaQueryWrapper<SocialStudent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(SocialStudent::getScore);
        List<SocialStudent> socialStudents = socialStudentService.list(queryWrapper);
        List<User> users = userService.list();
        List<SocialClazz> clazzList = socialClazzService.list();
        List<SocialStudentRankCacheBo> cacheBos = socialStudents.stream().map(socialStudent -> {
            SocialStudentRankCacheBo bo = new SocialStudentRankCacheBo();
            BeanUtils.copyProperties(socialStudent, bo);
            users.stream().filter(user -> user.getAccount().equals(socialStudent.getAccount())).findFirst().ifPresent(user -> {
                if (Objects.nonNull(user.getAvatar())) {
                    Image image = imageService.getImageById(user.getAvatar());
                    if (Objects.nonNull(image)) {
                        bo.setAvatarUrl(image.getRemoteFileUrl());
                    }
                }
                bo.setNickname(user.getNickname());
            });
            clazzList.stream().filter(socialClazz -> socialClazz.getId().equals(socialStudent.getClazzId())).findFirst().ifPresent(socialClazz -> {
                bo.setClazzId(socialClazz.getId());
                bo.setProjectId(socialClazz.getProjectId());
            });
            return bo;
        }).collect(Collectors.toList());
        redisUtil.setCacheObject(CachePrefix.STU_SCORE_SOCIAL_RANK.getPrefix(), cacheBos, 25, TimeUnit.HOURS);
    }


    /**
     * 从缓存获取学生排行信息
     * @return 学生排行信息
     */
    public List<StudentRankCacheBo> getStudentRankCacheBo() {
        List<StudentRankCacheBo> bos = redisUtil.getCacheObject(CachePrefix.STU_SCORE_RANK.getPrefix());
        while (bos == null) {
            this.saveStudentRankToCache();
            bos = redisUtil.getCacheObject(CachePrefix.STU_SCORE_RANK.getPrefix());
        }
        return bos;
    }

    /**
     * 从缓存获取学生排行信息
     * @return 学生排行信息
     */
    public List<SocialStudentRankCacheBo> getSocialStudentRankCacheBo() {
        List<SocialStudentRankCacheBo> bos = redisUtil.getCacheObject(CachePrefix.STU_SCORE_SOCIAL_RANK.getPrefix());
        while (bos == null) {
            this.saveSocialStudentRankToCache();
            bos = redisUtil.getCacheObject(CachePrefix.STU_SCORE_SOCIAL_RANK.getPrefix());
        }
        return bos;
    }

    /**
     * 获取所有学生积分排行榜
     * @param currUserId 当前用户ID
     * @return 所有学生积分排行榜
     */
    @Override
    public StuRankResp getAllStudentRank(Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        // 从缓存中获取排行数据
        List<StudentRankCacheBo> bos = this.getStudentRankCacheBo();
        // 构造结果集
        return this.buildStudentRankResp(bos, userCacheBo.getAccount());
    }

    /**
     * 获取班级学生积分排行榜
     * @param currUserId 当前用户ID
     * @return 班级学生积分排行榜
     */
    @Override
    public StuRankResp getClazzStudentRank(Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        // 从缓存中获取排行数据
        List<StudentRankCacheBo> bos = this.getStudentRankCacheBo();
        // 过滤同班级
        Student student = studentService.getStudentByAccount(userCacheBo.getAccount());
        bos = bos.stream().filter(studentRankCacheBo -> studentRankCacheBo.getClazzId().equals(student.getClazzId())).collect(Collectors.toList());
        // 构造结果集
        return this.buildStudentRankResp(bos, userCacheBo.getAccount());
    }

    /**
     * 获取专业学生积分排行榜
     * @param currUserId 当前用户ID
     * @return 专业学生积分排行榜
     */
    @Override
    public StuRankResp getMajorStudentRank(Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        // 从缓存中获取排行数据
        List<StudentRankCacheBo> bos = this.getStudentRankCacheBo();
        // 过滤同专业
        Student student = studentService.getStudentByAccount(userCacheBo.getAccount());
        bos = bos.stream().filter(studentRankCacheBo -> studentRankCacheBo.getMajorId().equals(student.getMajorId())).collect(Collectors.toList());
        // 构造结果集
        return this.buildStudentRankResp(bos, userCacheBo.getAccount());
    }

    /**
     * 获取学院学生积分排行榜
     * @param currUserId 当前用户ID
     * @return 学院学生积分排行榜
     */
    @Override
    public StuRankResp getCollegeStudentRank(Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        // 从缓存中获取排行数据
        List<StudentRankCacheBo> bos = this.getStudentRankCacheBo();
        // 过滤同学院
        Student student = studentService.getStudentByAccount(userCacheBo.getAccount());
        bos = bos.stream().filter(studentRankCacheBo -> studentRankCacheBo.getCollegeId().equals(student.getCollegeId())).collect(Collectors.toList());
        // 构造结果集
        return this.buildStudentRankResp(bos, userCacheBo.getAccount());
    }

    /**
     * 构造学生积分排行响应结果
     * @param bos     学生排行数据
     * @param account 当前登录用户账号
     * @return 响应结果
     */
    public StuRankResp buildStudentRankResp(List<StudentRankCacheBo> bos, String account) {
        // 查找自身排行
        Optional<StudentRankCacheBo> optional = bos.stream().filter(studentRankCacheBo -> studentRankCacheBo.getAccount().equals(account)).findFirst();
        // 构造结果
        StuRankResp resp = new StuRankResp();
        // 获取当前用户排名
        if (optional.isPresent()) {
            StudentRankCacheBo studentRankCacheBo = optional.get();
            int rank = bos.indexOf(studentRankCacheBo) + 1;
            resp.setNickname(studentRankCacheBo.getNickname());
            resp.setAvatarUrl(studentRankCacheBo.getAvatarUrl());
            resp.setScore(studentRankCacheBo.getScore());
            resp.setRank(rank);
        } else {
            resp.setRank(-1);
        }
        // 构造排行榜列表
        List<StuRankResp.RankListResp> listResps = new ArrayList<>();
        for (int i = 0; i < (Math.min(bos.size(), 50)); i++) {
            StudentRankCacheBo bo = bos.get(i);
            StuRankResp.RankListResp rankListResp = new StuRankResp.RankListResp();
            BeanUtils.copyProperties(bo, rankListResp);
            rankListResp.setRank(i + 1);
            listResps.add(rankListResp);
        }
        resp.setRankList(listResps);
        return resp;

    }

    /**
     * 构建社会化学员排行榜响应
     * @param bos     排行榜数据
     * @param account 当前用户账号
     * @return 排行榜响应数据
     */
    public StuRankResp buildSocialStudentRankResp(List<SocialStudentRankCacheBo> bos, String account) {
        // 查找自身排行
        Optional<SocialStudentRankCacheBo> optional = bos.stream().filter(socialStudentRankCacheBo -> socialStudentRankCacheBo.getAccount().equals(account)).findFirst();
        // 构造结果
        StuRankResp resp = new StuRankResp();
        // 获取当前用户排名
        if (optional.isPresent()) {
            SocialStudentRankCacheBo socialStudentRankCacheBo = optional.get();
            int rank = bos.indexOf(socialStudentRankCacheBo) + 1;
            resp.setNickname(socialStudentRankCacheBo.getNickname());
            resp.setAvatarUrl(socialStudentRankCacheBo.getAvatarUrl());
            resp.setScore(socialStudentRankCacheBo.getScore());
            resp.setRank(rank);
        } else {
            resp.setRank(-1);
        }
        // 构造排行榜列表
        List<StuRankResp.RankListResp> listResps = new ArrayList<>();
        for (int i = 0; i < (Math.min(bos.size(), 50)); i++) {
            SocialStudentRankCacheBo bo = bos.get(i);
            StuRankResp.RankListResp rankListResp = new StuRankResp.RankListResp();
            BeanUtils.copyProperties(bo, rankListResp);
            rankListResp.setRank(i + 1);
            listResps.add(rankListResp);
        }
        resp.setRankList(listResps);
        return resp;
    }

    /**
     * 获取所有社会化学员积分排行榜
     * @param currUserId 当前登录用户ID
     * @return 所有社会化学员积分排行榜
     */
    @Override
    public StuRankResp getAllSocialStudentRank(Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        // 从缓存中获取排行数据
        List<SocialStudentRankCacheBo> bos = this.getSocialStudentRankCacheBo();
        // 构造结果集
        return this.buildSocialStudentRankResp(bos, userCacheBo.getAccount());
    }

    /**
     * 获取班级社会化学员积分排行榜
     * @param currUserId 当前登录用户ID
     * @return 班级社会化学员积分排行榜
     */
    @Override
    public StuRankResp getClazzSocialStudentRank(Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        // 从缓存中获取排行数据
        List<SocialStudentRankCacheBo> bos = this.getSocialStudentRankCacheBo();
        // 过滤同社会化班级
        SocialStudent socialStudent = socialStudentService.getSocialStudentByAccount(userCacheBo.getAccount());
        bos = bos.stream().filter(socialStudentRankCacheBo -> socialStudentRankCacheBo.getClazzId().equals(socialStudent.getClazzId())).collect(Collectors.toList());
        // 构造结果集
        return this.buildSocialStudentRankResp(bos, userCacheBo.getAccount());
    }

    /**
     * 获取所有社会化学员积分排行榜
     * @param currUserId 当前登录用户ID
     * @return 所有社会化学员积分排行榜
     */
    @Override
    public StuRankResp getProjectSocialStudentRank(Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        // 从缓存中获取排行数据
        List<SocialStudentRankCacheBo> bos = this.getSocialStudentRankCacheBo();
        // 过滤同社会化项目
        SocialStudent socialStudent = socialStudentService.getSocialStudentByAccount(userCacheBo.getAccount());
        SocialClazz socialClazz = socialClazzService.getById(socialStudent.getClazzId());
        bos = bos.stream().filter(socialStudentRankCacheBo -> socialStudentRankCacheBo.getProjectId().equals(socialClazz.getProjectId())).collect(Collectors.toList());
        // 构造结果集
        return this.buildSocialStudentRankResp(bos, userCacheBo.getAccount());
    }

    /**
     * 保存学生积分
     * @param dto        学生积分数据
     * @param currUserId 当前登录用户ID
     * @return 处理结果
     */
    @Override
    @Transactional
    public boolean saveScore(ScoreUpdateDto dto, Long currUserId) {
        // 保存积分信息
        Score score = new Score();
        BeanUtils.copyProperties(dto, score);
        score.setCreateUser(currUserId);
        score.setUpdateUser(currUserId);
        if (!this.save(score)) {
            throw new DatabaseOperationException("保存积分信息失败");
        }
        // 获取当前用户信息
        boolean flag;
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        if (userCacheBo.getUserType().equals(AppProperties.STUDENT_USER_TYPE)) {
            Student student = studentService.getStudentByAccount(userCacheBo.getAccount());
            student.setScore(student.getScore() + score.getNum());
            student.setUpdateUser(currUserId);
            flag = studentService.updateById(student);
        } else {
            SocialStudent socialStudent = socialStudentService.getSocialStudentByAccount(userCacheBo.getAccount());
            socialStudent.setScore(socialStudent.getScore() + score.getNum());
            socialStudent.setUpdateUser(currUserId);
            flag = socialStudentService.updateById(socialStudent);
        }
        if (!flag) {
            throw new DatabaseOperationException("数据库操作失败");
        }
        return true;
    }

    /**
     * 获取指定范围获得积分数
     * @param account 用户账号
     * @param range   距今时间范围，单位天
     * @return 获得积分数
     */
    @Override
    public Integer getObtainScoreByRange(String account, Integer range) {
        // 查询指定范围积分获取记录
        DayRange dayRange = DateTimeUtil.dayRange(range);
        List<Score> scoreList = this.list(new LambdaQueryWrapper<Score>()
                .eq(Score::getAccount, account)
                .ge(Score::getCreateTime, dayRange.getStartTime())
                .le(Score::getCreateTime, dayRange.getEndTime()));
        return scoreList.stream().mapToInt(Score::getNum).sum();
    }

    @Override
    public PageR<ScoreRecordResp> getScoreRecordByPage(Integer pageNum, Integer pageSize, String currAccount) {
        Page<Score> page = new Page<>(pageNum, pageSize);
        this.page(page, new LambdaQueryWrapper<Score>().eq(Score::getAccount, currAccount).orderByDesc(Score::getCreateTime));
        List<ScoreRecordResp> collect = page.getRecords().stream().map(score -> {
            ScoreRecordResp resp = new ScoreRecordResp();
            BeanUtils.copyProperties(score, resp);
            resp.setTime(score.getCreateTime());
            return resp;
        }).collect(Collectors.toList());

        PageR<ScoreRecordResp> pageR = new PageR<>();
        BeanUtils.copyProperties(page, pageR, "records");
        pageR.setRecords(collect);
        return pageR;
    }

}




